package com.erp.app.common.util;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.DecimalFormat;

import com.erp.app.common.ProjectConstants;

/**
 * DocumentNumberGenerator is the utility class for generate document number.
 * The generation algorithm uses the table T_SEQUENCE which contains the last
 * document number used. Apart from the generation algorithm,
 * DocumentNumberGenerator also contains a few utility functions which are also
 * useful in document nubmer generation.
 * <p>
 * DocumentNumberGenerator should be called within a transaction context and
 * with the same connection used throughtout. Thus, if an exception occurs, the
 * running number generated can be rolled back.
 * <p>
 * Also an instance of DocumentNumberGenerator should be used to generated one
 * number.
 *
 *
 */

public class DocumentNumberGenerator {

    /**
     * Default character used for stuffing.
     *
     */
    final protected static String DEFAULT_CHAR = "-";

    /**
     * if year is ignored.
     *
     */
    final public static String YEAR_IGNORED = "*";
    public static final String PREFIX_IGNORED = "NA";
    public static final String INCLUDE_YEAR = ProjectConstants.GENERIC_YES;
    public static final String EXCLUDE_YEAR = ProjectConstants.GENERIC_NO;
    /***************************************************************************
     * final public static String DOC_TYPE_ITEM_MASTER = "ITEM"; final public
     * static String FIELD_NAME_INT_ITEM_CODE = "INT_ITEM_CODE"; final public
     * static int FIELD_NAME_INT_ITEM_CODE_LENGTH = 7;
     **************************************************************************/

    //
    //final public static String DOC_TYPE_GRN = ProjectConstants.DOC_TYPE_GRN;
    //final public static String FIELD_NAME_GRN_NO = "GRN_NO";
    //final public static int FIELD_NAME_GRN_NO_LENGTH = 6;

    protected String companyCode;
    protected String docType;
    protected String fieldName;
    protected String year;
    protected String userId;
    protected String prefixCode;
    protected Connection conn;
    private int defaultNumberDigits = 6;
    private int numYear = 2;
    private String yearDelimiter = "/";
    private boolean includeYear = false;

    public DocumentNumberGenerator(String companyCode2
    		, String docType2
    		, String fieldName
    		, String userId2
    		, String prefixCode2,
            Connection conn2) {

        this(companyCode2, docType2, fieldName, null, userId2, prefixCode2, conn2);
    }

    public DocumentNumberGenerator(String companyCode2
    		, String docType2
    		, String fieldName
    		, String userId2
    		, Connection conn2) {

        this(companyCode2, docType2, fieldName, null, userId2, null, conn2);
    }

    public DocumentNumberGenerator(String companyCode2
    		, String docType2
    		, String fieldName
    		, String year2
    		, String userId2,
            String prefixCode2
            , Connection conn2) {
        this.companyCode = companyCode2;
        this.docType = docType2;
        this.fieldName = fieldName;
        this.year = year2;
        this.userId = userId2;
        this.prefixCode = prefixCode2;
        this.conn = conn2;

        if (year == null) {
            this.year = getCurrentYear();
        }
    }

    public String formatSequence(BigDecimal sequence, int length) {
        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < length; i++) {
            sb.append("0");
        }
        DecimalFormat df = new DecimalFormat(sb.toString());
        return df.format(sequence);
    }

    public String getSequence(int length) {
        return this.formatSequence(getSequenceNo(), length);
    }

    public String getYear() {
        if (year == null) {
            year = getCurrentYear();
        }
        return year.substring(4 - getNumYear(), 4);
    }

    public static String fixLength(String inStr, int length) {
        return fixLength(inStr, length, false);
    }

    public static String fixLength(String inStr, int length, boolean wantTail) {

        String retStr = "";
        StringBuffer sb = new StringBuffer("");
        if (inStr != null) {
            sb = sb.append(inStr);
        }

        for (int i = 0; sb.length() < length; i++) {
            sb.insert(0, DEFAULT_CHAR);
        }

        return (wantTail ? sb.substring(sb.length() - length, sb.length()) : sb.substring(0, length));
    }

    protected String getCurrentYear() {

        String year = null;

        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = conn.createStatement();
            rs = stmt.executeQuery(" select to_char(sysdate, 'YYYY') from dual ");
            if (rs != null && rs.next()) {
                year = rs.getString(1);
            }
        } catch (Exception e) {
            System.err.println("DocumentNumberGenerator.getSequence() Exception: " + e.getMessage());
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (Exception e) {
            }
            try {
                if (stmt != null)
                    stmt.close();
            } catch (Exception e) {
            }
        }

        return year;
    }

    /**
     * Updates and returns the next LAST_NO and LAST_CHAR from T_SEQUENCE
     * @return the next LAST_NO
     */
    protected BigDecimal getSequenceNo() {
        return this.getDocumentNumber().getLastNo();
    }

    /**
     * Updates and returns the next LAST_NO and LAST_CHAR from T_SEQUENCE
     * This method is refactored from the original getSequenceNo().
     *
     * First used in Vendor Master module.
     *
     * @return a DocumentNumberVo, which contains the next LAST_NO and LAST_CHAR
     */
    public DocumentNumberVo getDocumentNumber() {
        DocumentNumberVo retValue = new DocumentNumberVo();
        PreparedStatement pStmt = null;
        ResultSet rs = null;

        if (companyCode == null || docType == null || year == null) {
            // all argument must not be null
            throw new IllegalArgumentException();
        }

        String updateStr = " update t_sequence " + " set last_no = decode((length(to_char(last_no + 1)) - number_length), 1, 1, last_no + 1), last_char = decode((length(to_char(last_no + 1)) - number_length), 1, chr(ascii(last_char) + 1), last_char), update_date = sysdate, update_user = ? "
                + " where company_code = ? " + " and doc_type = ? " + " and field_name = ? " + " and year_no = ? "+" and prefix_code = ? ";

        String insertStr = " insert into t_sequence (company_code, doc_type, field_name, prefix_code, year_no, last_no, number_length, create_date, create_user) "
                + " values (?, ?, ?, ?, ?, 1, ?, sysdate, ?) ";

        String selectStr = " select last_no, last_char " + " from t_sequence " + " where company_code = ? " + " and doc_type = ? "
                + " and field_name = ? " + " and year_no = ? "+" and prefix_code = ? ";

        try {
            // update lock
            pStmt = conn.prepareStatement(updateStr);
            pStmt.setString(1, userId);
            pStmt.setString(2, companyCode);
            pStmt.setString(3, docType);
            pStmt.setString(4, fieldName);
            pStmt.setString(5, year);
            pStmt.setString(6, prefixCode);

            int count = pStmt.executeUpdate();
            pStmt.close();
            // conn.commit();

            // insert if not found
            if (count == 0) {
                int i = 1;
                pStmt = conn.prepareStatement(insertStr);
                pStmt.setString(i++, companyCode);
                pStmt.setString(i++, docType);
                pStmt.setString(i++, fieldName);
                pStmt.setString(i++, prefixCode);
                pStmt.setString(i++, year);
                pStmt.setInt(i++, defaultNumberDigits);
                pStmt.setString(i++, userId);
                pStmt.executeUpdate();
                pStmt.close();
                // conn.commit();
            }

            // retrieve the sequence no.
            pStmt = conn.prepareStatement(selectStr);
            pStmt.setString(1, companyCode);
            pStmt.setString(2, docType);
            pStmt.setString(3, fieldName);
            pStmt.setString(4, year);
            pStmt.setString(5, prefixCode);
            rs = pStmt.executeQuery();
            // conn.commit();
            if (rs != null && rs.next()) {
                retValue.setLastNo(rs.getBigDecimal(1));
                retValue.setLastChar(rs.getString(2));
            }
        } catch (Exception e) {
            System.err.println("DocumentNumberGenerator.getSequence() Exception: " + e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
            } catch (Exception e) {
            }

            try {
                if (pStmt != null)
                    pStmt.close();
            } catch (Exception e) {
            }
        }
        return retValue;
    }

    public String getAutoGenSeasonMasterNo(String prefix,int length){
        return (prefix + getSequence(length));
    }

    public String getAutoAttachmentLineNo(String prefix,int length){
        return  getSequence(length);
    }

    public void setCompanyCode(String companyCode) {
        this.companyCode = companyCode;
    }

    public void setDocType(String docType) {
        this.docType = docType;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public void setPrefixCode(String prefixCode) {
        this.prefixCode = prefixCode;
    }

    public int getNumYear() {
        return numYear;
    }

    public void setNumYear(int numYear) {
        this.numYear = numYear;
    }

    public void setYearDelimiter(String yearDelimiter) {
        this.yearDelimiter = yearDelimiter;
    }

    public void setIncludeYear(boolean includeYear) {
        this.includeYear = includeYear;
    }

    //quality
    public String getAutoQualityCode(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
    
    //setup module
    public String getSequenceWithPrefix(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
    public String getAutoGenFactAuditCode(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
    public String getAutoGenVendorCode(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
    public String getAutoGenCustCode(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
    public String getAutoGenCode(final int length){
    	return this.prefixCode+getSequence(length);
    }
    
}